package com.pearreview.ui;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.URI;

import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.SwingWorker;

public abstract class ImageLoader extends SwingWorker<Icon, Void> 
		implements Serializable {
	private static final long serialVersionUID = -5763968940901598723L;
	private URI loadPath;
	private int width;
	private int height;
	
	public ImageLoader() {
		this(null);
	}
	
	public ImageLoader(int width, int height) {
		this(null, width, height);
	}
	
	public ImageLoader(URI uri) {
		this(uri, 100, 100);
	}
	
	public ImageLoader(URI uri, int width, int height) {
		this.setLoadPath(uri);
		this.setWidth(width);
		this.setHeight(height);
	}
	
	public static Icon getErrorIcon() {
		return new ErrorIcon();
	}
	
	@Override
	protected Icon doInBackground() throws Exception {
		File file;
		synchronized(getLoadPath()) {
			file = new File(getLoadPath());
		}
		
		BufferedImage icon = createImageIcon(file);
		
		return new ImageIcon(getScaledImage(icon, getWidth(), getHeight()));
	}
	
	@Override
	protected void done() {
		try {
			success(get());
		} catch(Exception e) {
			error(e, getErrorIcon());
		}
	}
	
	public abstract void success(Icon icon);
	
	public abstract void error(Throwable e, Icon icon);
	
	private BufferedImage createImageIcon(File file) throws IOException {
		return ImageIO.read(file);
	}
	
	public BufferedImage getScaledImage(BufferedImage image, int w, int h) {
		int type = (image.getTransparency() == Transparency.OPAQUE) ?
				BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
		
		int tWidth = w;
		int tHeight = h;
		int iWidth = image.getWidth(null);
		int iHeight = image.getHeight(null);
		
		double tRatio = (double)tWidth / (double)tHeight;
		double iRatio = (double)iWidth / (double)iHeight;
		if (tRatio < iRatio) {
		  tHeight = (int)(tWidth / iRatio);
		} else {
		  tWidth = (int)(tHeight * iRatio);
		}
		
		BufferedImage ret = new BufferedImage(tWidth, tHeight, type);
		Graphics2D g2 = ret.createGraphics();
		g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 
				RenderingHints.VALUE_INTERPOLATION_BICUBIC);
		g2.drawImage(image, 0, 0, tWidth, tHeight, null);
		g2.dispose();
		
		return ret;
	}
	
	public URI getLoadPath() {
		return loadPath;
	}
	
	public void setWidth(int width) {
		this.width = width;
	}

	public int getWidth() {
		return width;
	}

	public void setHeight(int height) {
		this.height = height;
	}

	public int getHeight() {
		return height;
	}

	public void setLoadPath(URI loadPath) {
		this.loadPath = loadPath;
	}

	private static class ErrorIcon implements Icon, Serializable {
		private static final long serialVersionUID = 3992236293046597530L;
		private int width = 32;
		private int height = 32;
		private BasicStroke stroke = new BasicStroke(4);
		@Override
		public void paintIcon(Component c, Graphics g, int x, int y) {
			Graphics2D g2d = (Graphics2D) g.create();
			
			g2d.setColor(Color.WHITE);
			g2d.fillRect(x +1 ,y + 1,width -2 ,height -2);
			
			g2d.setColor(Color.BLACK);
			g2d.drawRect(x +1 ,y + 1,width -2 ,height -2);
			
			g2d.setColor(Color.RED);
			
			g2d.setStroke(stroke);
			g2d.drawLine(x +10, y + 10, x + width -10, y + height -10);
			g2d.drawLine(x +10, y + height -10, x + width -10, y + 10);
			
			g2d.dispose();
		}
		@Override
		public int getIconWidth() {
			return width;
		}
		@Override
		public int getIconHeight() {
			return height;
		}
	}
}
